// node test_chain_relaunch.js
// 目标：1) 启动 launcher 并在其中点击入口；2) 读取被 launcher 拉起的 studio 的“动态参数”；
//      3) 结束这份不可控的 studio；4) 用相同参数 + Playwright 重新 launch 可控的 studio；
//      5) 在 studio 中继续自动化。
// 说明：这是“顺序控制”，不会同时控制两个窗口；切到 studio 后就不再操作 launcher。

const { _electron: electron } = require('playwright');
const cp = require('child_process');

const LAUNCHER = String.raw`C:\Program Files\Max-Optics\MaxOpticsStudio\2025R1\v3.3.3.0_09100454\gui\launcher\MaxOpticsLauncher.exe`;
const STUDIO   = String.raw`C:\Program Files\Max-Optics\MaxOpticsStudio\2025R1\v3.3.3.0_09100454\gui\mos\MaxOpticsStudio.exe`;

// 你 launcher 页面的选择器（与之前 demo/test_launcher.js 一致）:
const SEL = {
  main: {
    help:     'xpath=//*[contains(text(),"Help")]',
    examples: 'xpath=//*[contains(text(),"Examples")]',
    // 这里是会触发 studio 的入口（举例 Bend）；如你的真实入口不同，请替换
    entry:    'xpath=//*[contains(text(),"Bend")]',
  },
  studio: {
    // 到了 studio 的点击示例
    view:     'xpath=//*[contains(text(),"Analysis")]',
  }
};

const sleep = (ms) => new Promise(r => setTimeout(r, ms));

// 简单的命令行解析：把 wmic 的 CommandLine 拆成 argv（保留引号分组）
function splitCmdLine(cmdline) {
  const re = /"([^"]+)"|(\S+)/g;
  const argv = [];
  let m;
  while ((m = re.exec(cmdline)) !== null) {
    argv.push(m[1] || m[2]);
  }
  return argv;
}

// 查找由 launcher 拉起的 studio 进程（返回 {pid, cmdline}，按创建时间“看起来最新”的一条）
function findSpawnedStudio() {
  try {
    // wmic 一次性拿到两个字段，避免再查
    const out = cp.execSync(
      `wmic process where (name='MaxOpticsStudio.exe') get ProcessId,CommandLine /FORMAT:LIST`,
      { encoding: 'utf8' }
    );
    // 拆分多条记录
    const blocks = out.split(/\r?\n\r?\n/).map(x => x.trim()).filter(Boolean);
    // 简单取最后一条（通常是最新的）；如需更严谨可加创建时间排序
    const last = blocks[blocks.length - 1] || '';
    const pid = (last.match(/ProcessId=(\d+)/) || [])[1];
    const cmd = (last.match(/CommandLine=(.+)/) || [])[1];
    if (pid && cmd) return { pid: parseInt(pid, 10), cmdline: cmd };
  } catch {}
  return null;
}

function killPid(pid) {
  try { cp.execSync(`taskkill /PID ${pid} /F`); } catch {}
}

async function firstWindow(app) {
  const wins = app.windows();
  return wins[0] || await app.firstWindow();
}

async function safeClick(page, selector, timeout = 8000) {
  await page.bringToFront();
  await page.locator(selector).click({ timeout });
}

(async () => {
  let launcherApp = null;
  let studioApp   = null;
  try {
    // 1) 启动 launcher 并点击入口
    console.log('[launcher] launch:', LAUNCHER);
    launcherApp = await electron.launch({ executablePath: LAUNCHER, args: [] });
    console.log('[launcher] 等待启动...');
    await sleep(15000);

    const mainPage = await firstWindow(launcherApp);

    console.log('[launcher] click Help');
    await safeClick(mainPage, SEL.main.help, 6000);

    console.log('[launcher] click Examples');
    await safeClick(mainPage, SEL.main.examples, 6000);

    console.log('[launcher] click Entry (trigger Studio)');
    await safeClick(mainPage, SEL.main.entry, 8000);

    // 2) 等待 Studio 进程出现，并拿到“动态参数”
    console.log('[studio-spawn] 等待由 launcher 拉起的 studio 出现...');
    let info = null, deadline = Date.now() + 15000;
    while (Date.now() < deadline) {
      info = findSpawnedStudio();
      if (info) break;
      await sleep(500);
    }
    if (!info) throw new Error('未发现由 launcher 拉起的 Studio 进程');
    console.log(`[studio-spawn] 捕获到 Studio pid=${info.pid}`);
    // 解析它的命令行，提取“动态参数”
    const argv = splitCmdLine(info.cmdline);
    // argv[0] 通常是 exe 路径，后续是参数
    const exe = argv[0];
    const argsFromLauncher = argv.slice(1);
    console.log('[studio-spawn] exe =', exe);
    console.log('[studio-spawn] args =', JSON.stringify(argsFromLauncher));

    // 3) 结束这份“不可控”的 studio（单实例会挡住我们重新 launch）
    console.log('[studio-spawn] 结束由 launcher 拉起的 studio，以便重新受控启动');
    killPid(info.pid);
    await sleep(1500);

    // 4) 用相同参数 + Playwright 重新 launch 可控的 studio
    console.log('[studio] 受控 launch:', STUDIO);
    // 这里用我们知道的 studio 可执行文件（通常与 exe 一致），并复用“动态参数”
    studioApp = await electron.launch({ executablePath: STUDIO, args: argsFromLauncher });
    console.log('[studio] 等待启动...');
    await sleep(8000);

    const studioPage = await firstWindow(studioApp);

    // 5) 在 studio 中继续自动化
    console.log('[studio] click View');
    await safeClick(studioPage, SEL.studio.view, 10000);

    console.log('✅ OK：已完成 launcher→studio 的顺序控制，studio 使用从 launcher 入口继承的动态参数。');
  } catch (e) {
    console.error('❌ 失败：', e);
    process.exitCode = 1;
  } finally {
    // 需求是切到 studio 后基本不回 launcher；如需保留观察，可注释关闭
    try { if (studioApp)   await studioApp.close(); } catch {}
    try { if (launcherApp) await launcherApp.close(); } catch {}
  }
})();
